module klang.编译.路径.路径_;
import 全局;
import std.conv: ·to = to;
import std.functional: ·fd = toDelegate;
import klang.编译.类型.枚举值.枚举值_: 字符_, 比较结果_;
import 核心 = klang.编译.核心.核心_;
const dstring 目录分割符 = "/"d;
const dstring 备用目录分割符 = "\\"d;
const dstring Url分隔符 = "://"d;
const 正则_!(dchar) 反斜杠正则;
const 正则_!(dchar) 相对路径正则;
// check path for these segments: '', '.'. '..'
const 正则_!(dchar) 相对路径段正则;
static this() {
    反斜杠正则 = ·regex(r"\\"d, "g"c);
    相对路径正则 = ·regex(r"^\.\.?($|[\\/])"d, "g"c);
    相对路径段正则 = ·regex(r"(?:\/\/)|(?:^|\/)\.\.?(?:$|\/)"d, "g"c);
}
// 使用别名路径的必须是经过正规化的
class 路径_ {
    dstring 原型;
    this() { this.原型 = ··null!(dstring); }
    immutable this() { this.原型 = ··null!(immutable(dstring)); }
    shared this() { this.原型 = ··null!(shared(dstring)); }
    this(dstring ·this) {
        this.原型 = ·this;
    }
    const this(const(dstring) ·this) {
        this.原型 = ·this;
    }
    immutable this(immutable(dstring) ·this) {
        this.原型 = ·this;
    }
    shared this(shared(dstring) ·this) {
        this.原型 = ·this;
    }
    ·U opCast(·U)() {
        if (this is null)
            return null;
        ·U ·ret = new ·U();
        ·ret.原型 = ·to!(typeof(·ret.原型))(原型);
        return ·ret;
    }
    alias 原型 this;
}
struct 路径和路径片段_ {
    dstring 路径;
    dstring[] 片段;
    this(dstring 路径, dstring[] 片段) {
        this.路径 = 路径;
        this.片段 = 片段;
    }
}
路径_ 创建路径(dstring 路径名) {
    dstring 文件名 = .正规化路径(路径名);
    路径_ 路径 = new 路径_(文件名);
    return 路径;
}
bool 是目录分割符(immutable(dchar) 字符代码) {
    return 字符代码 == 字符_.斜杠 || 字符代码 == 字符_.反斜杠;
}
bool 是URL(dstring 路径) {
    return .获取编码根长度(路径) < 0;
}
bool 文件扩展名是(dstring path, dstring extension) {
    return path.length > extension.length && 核心.结尾包含(path, extension);
}
/**
 * Determines whether a path is an absolute disk path (e.g. starts with `/`, or a dos path
 * like `c:`, `c:\` or `c:/`).
 */
bool 是根磁盘路径(dstring 路径) {
    return .获取编码根长度(路径) > 0;
}
/**
 * Determines whether a path consists only of a path root.
 */
bool 是磁盘路径根(dstring 路径) {
    long 根长度 = .获取编码根长度(路径);
    return 根长度 > 0 && 根长度 == 路径.length;
}
/**
 * Determines whether a path starts with an absolute path component (i.e. `/`, `c:/`, `file://`, etc.).
 *
 * ```k
 * // POSIX
 * 是绝对路径("/path/to/file.ext") == 真
 * // DOS
 * 是绝对路径("c:/path/to/file.ext") == 真
 * // URL
 * 是绝对路径("file:///path/to/file.ext") == 真
 * // Non-absolute
 * 是绝对路径("path/to/file.ext") == 假
 * 是绝对路径("./path/to/file.ext") == 假
 * ```
 */
bool 是绝对路径(dstring 路径) {
    return .获取编码根长度(路径) != 0;
}
/**
 * Determines whether a path starts with a relative path component (i.e. `.` or `..`).
 */
bool 是相对路径(dstring 路径) {
    return !相对路径正则.p·匹配(路径).empty();
}
bool 有扩展名(dstring 文件名称) {
    return 核心.文本包含(.获取基础文件名称(文件名称), "."d);
}
dstring 获取基础文件名称(dstring 路径, dstring[] 扩展名组 = ··null!(dstring[]), bool 忽略大小写 = true) {
    路径 = .正规化反斜杠(路径);
    long 根长度 = .获取根长度(路径);
    if (根长度 == 路径.length) {
        return ""d;
    }
    路径 = .删除尾随路径分隔符(路径);
    dstring 文件名 = 路径[核心.最大(.获取根长度(路径), 核心.从后查找索引(路径, .目录分割符) + 1) .. $];
    if ((扩展名组 !is ··null!(dstring[])) && 忽略大小写) {
        dstring 扩展名 = .获取任意一个扩展名从路径(文件名, 扩展名组, 忽略大小写);
        return 文件名[0 .. 文件名.length - 扩展名.length];
    }
    return 文件名;
}
/**
 * Normalize path separators, converting `\` into `/`.
 */
dstring 正规化反斜杠(dstring 路径) {
    dstring 输出 = ""d;
    int 开始 = 0;
    {
        for (int i = 0; i < 路径.length; ++i) {
            immutable(dchar) ch = 路径[i];
            if (ch == 字符_.反斜杠) {
                输出 ~= 路径[开始 .. i] ~ .目录分割符;
                开始 = ++i;
            }
        }
    }
    if (开始 < 路径.length) {
        输出 ~= 路径[开始 .. $];
    }
    return 输出;
}
/**
 * Returns length of the root part of a path or URL (i.e. length of "/", "x:/", "//server/share/, file:///user/files").
 *
 * For example:
 * ```k
 * 获取根长度("a") == 0                   // ""
 * 获取根长度("/") == 1                   // "/"
 * 获取根长度("c:") == 2                  // "c:"
 * 获取根长度("c:d") == 0                 // ""
 * 获取根长度("c:/") == 3                 // "c:/"
 * 获取根长度("c:\\") == 3                // "c:\\"
 * 获取根长度("//server") == 7            // "//server"
 * 获取根长度("//server/share") == 8      // "//server/"
 * 获取根长度("\\\\server") == 7          // "\\\\server"
 * 获取根长度("\\\\server\\share") == 8   // "\\\\server\\"
 * 获取根长度("file:///path") == 8        // "file:///"
 * 获取根长度("file:///c:") == 10         // "file:///c:"
 * 获取根长度("file:///c:d") == 8         // "file:///"
 * 获取根长度("file:///c:/path") == 11    // "file:///c:/"
 * 获取根长度("file://server") == 13      // "file://server"
 * 获取根长度("file://server/path") == 14 // "file://server/"
 * 获取根长度("http://server") == 13      // "http://server"
 * 获取根长度("http://server/path") == 14 // "http://server/"
 * ```
 */
long 获取根长度(dstring 路径) {
    long 根长度 = .获取编码根长度(路径);
    return 根长度 < 0 ? ~根长度 : 根长度;
}
/**
 * Combines paths. If a path is absolute, it replaces any previous path. Relative paths are not simplified.
 *
 * ```k
 * // Non-rooted
 * 组合路径("path", "to", "file.ext") == "path/to/file.ext"
 * 组合路径("path", "dir", "..", "to", "file.ext") == "path/dir/../to/file.ext"
 * // POSIX
 * 组合路径("/path", "to", "file.ext") == "/path/to/file.ext"
 * 组合路径("/path", "/to", "file.ext") == "/to/file.ext"
 * // DOS
 * 组合路径("c:/path", "to", "file.ext") == "c:/path/to/file.ext"
 * 组合路径("c:/path", "c:/to", "file.ext") == "c:/to/file.ext"
 * // URL
 * 组合路径("file:///path", "to", "file.ext") == "file:///path/to/file.ext"
 * 组合路径("file:///path", "file:///to", "file.ext") == "file:///to/file.ext"
 * ```
 */
dstring 组合路径(dstring 路径, dstring[] 部件组) {
    部件组 = 部件组.dup;
    if (路径 !is ··null!(dstring)) {
        路径 = .正规化反斜杠(路径);
    }
    foreach (dstring 相对路径; 部件组) {
        if (相对路径 is ··null!(dstring)) {
            continue;
        }
        相对路径 = .正规化反斜杠(相对路径);
        if ((路径 is ··null!(dstring)) || .获取根长度(相对路径) != 0) {
            路径 = 相对路径;
        }
        else {
            路径 = .确保尾随路径分隔符(路径) ~ 相对路径;
        }
    }
    return 路径;
}
/**
 * Reduce an array of path components to a more simplified path by navigating any
 * `"."` or `".."` entries in the path.
 */
dstring[] 缩减路径组件(dstring[] 组件组) {
    if (组件组 is ··null!(dstring[])) {
        return [];
    }
    dstring[] 结果 = [组件组[0]];
    {
        for (int i = 1; i < 组件组.length; ++i) {
            dstring 组件 = 组件组[i];
            if (组件 is ··null!(dstring)) {
                continue;
            }
            if (组件 == "."d) {
                continue;
            }
            if (组件 == ".."d) {
                if (结果.length > 1) {
                    if (结果[结果.length - 1] != ".."d) {
                        结果 = 结果[0 .. $-1];
                        continue;
                    }
                }
                else if (结果[0] !is ··null!(dstring)) {
                    continue;
                }
            }
            结果 = .追加!(dstring)(结果, 组件);
        }
    }
    return 结果;
}
/**
 * Determines whether a path has a trailing separator (`/` or `\\`).
 */
bool 有尾随路径分割符(dstring 路径) {
    return 路径.length > 0 && .是目录分割符(路径[路径.length - 1]);
}
dstring 确保尾随路径分隔符(dstring 路径) {
    if (!.有尾随路径分割符(路径)) {
        return 路径 ~ .目录分割符;
    }
    return 路径;
}
dstring 删除尾随路径分隔符(dstring 路径) {
    if (.有尾随路径分割符(路径)) {
        return 路径[0 .. $-1];
    }
    return 路径;
}
dstring 尝试获取扩展从文件名(dstring 路径, dstring 扩展名, bool delegate(dstring, dstring) 文本比较器) {
    if (!核心.开始包含(扩展名, "."d)) {
        扩展名 = "."d ~ 扩展名;
    }
    if (路径.length >= 扩展名.length && 路径[路径.length - 扩展名.length] == 字符_.点号) {
        dstring 路径的扩展 = 路径[路径.length - 扩展名.length .. $];
        if (文本比较器(路径的扩展, 扩展名)) {
            return 路径的扩展;
        }
    }
    return ""d;
}
dstring 获取任意一个扩展名从路径平台(dstring 路径, dstring[] 扩展组, bool delegate(dstring, dstring) 文本比较器) {
    foreach (dstring 扩展名; 扩展组) {
        dstring 结果 = .尝试获取扩展从文件名(路径, 扩展名, cast(bool delegate(dstring, dstring))(文本比较器));
        if (结果 !is ··null!(dstring)) {
            return 结果;
        }
    }
    return ""d;
}
dstring 获取任意一个扩展名从路径(dstring 路径, dstring[] 扩展组 = ··null!(dstring[]), bool 忽略大小写 = ··null!(bool)) {
    if (扩展组 !is ··null!(dstring[])) {
        return .获取任意一个扩展名从路径平台(.删除尾随路径分隔符(路径), 扩展组, 忽略大小写 ? cast(bool delegate(dstring, dstring))(·fd(&核心.相等文本不区分大小写)) : cast(bool delegate(dstring, dstring))(·fd(&核心.相等值!(dstring))));
    }
    dstring 基础文件名 = .获取基础文件名称(路径);
    long 扩展索引 = 核心.从后查找索引(基础文件名, "."d);
    if (扩展索引 >= 0) {
        return 基础文件名[扩展索引 .. $];
    }
    return ""d;
}
//// Path Formatting
/**
 * Formats a parsed path consisting of a root component (at index 0) and zero or more path
 * segments (at indices > 0).
 *
 * ```k
 * 获取路径从路径组件组(["/", "path", "to", "file.ext"]) == "/path/to/file.ext"
 * ```
 */
dstring 获取路径从路径组件组(dstring[] 路径组件组) {
    if (路径组件组.length == 0) {
        return ""d;
    }
    if (路径组件组[0] !is ··null!(dstring)) {
        dstring 根 = .确保尾随路径分隔符(路径组件组[0]);
        return 根 ~ 核心.连接(路径组件组[1 .. $], .目录分割符);
    }
    return 核心.连接(路径组件组, .目录分割符);
}
dstring[] 路径组件组(dstring 路径, long 根长度) {
    dstring 根 = 路径[0 .. 根长度];
    dstring[] 剩余 = 核心.分割(路径[根长度 .. $], [.目录分割符]);
    if (剩余.length != 0) {
        dstring 最后一个 = 核心.最后一个!(dstring)(剩余);
        if (最后一个 is ··null!(dstring)) {
            剩余 = 剩余[0 .. $-1];
        }
    }
    dstring[] 结果 = [根];
    return .追加!(dstring)(结果, 剩余);
}
/**
 * Parse a path into an array containing a root component (at index 0) and zero or more path
 * components (at indices > 0). The result is not normalized.
 * If the path is relative, the root component is `""`.
 * If the path is absolute, the root component includes the first path separator (`/`).
 *
 * ```k
 * // POSIX
 * 获取路径组件组("/path/to/file.ext") == ["/", "path", "to", "file.ext"]
 * 获取路径组件组("/path/to/") == ["/", "path", "to"]
 * 获取路径组件组("/") == ["/"]
 * // DOS
 * 获取路径组件组("c:/path/to/file.ext") == ["c:/", "path", "to", "file.ext"]
 * 获取路径组件组("c:/path/to/") == ["c:/", "path", "to"]
 * 获取路径组件组("c:/") == ["c:/"]
 * 获取路径组件组("c:") == ["c:"]
 * // URL
 * 获取路径组件组("http://typescriptlang.org/path/to/file.ext") == ["http://typescriptlang.org/", "path", "to", "file.ext"]
 * 获取路径组件组("http://typescriptlang.org/path/to/") == ["http://typescriptlang.org/", "path", "to"]
 * 获取路径组件组("http://typescriptlang.org/") == ["http://typescriptlang.org/"]
 * 获取路径组件组("http://typescriptlang.org") == ["http://typescriptlang.org"]
 * 获取路径组件组("file://server/path/to/file.ext") == ["file://server/", "path", "to", "file.ext"]
 * 获取路径组件组("file://server/path/to/") == ["file://server/", "path", "to"]
 * 获取路径组件组("file://server/") == ["file://server/"]
 * 获取路径组件组("file://server") == ["file://server"]
 * 获取路径组件组("file:///path/to/file.ext") == ["file:///", "path", "to", "file.ext"]
 * 获取路径组件组("file:///path/to/") == ["file:///", "path", "to"]
 * 获取路径组件组("file:///") == ["file:///"]
 * 获取路径组件组("file://") == ["file://"]
 * ```
 */
dstring[] 获取路径组件组(dstring 路径, dstring 当前路径 = ""d) {
    路径 = .组合路径(当前路径, [路径]);
    return .路径组件组(路径, .获取根长度(路径));
}
/**
 * Returns the path except for its basename. Semantics align with NodeJS's `path.dirname`
 * except that we support URLs as well.
 *
 * ```ts
 * // POSIX
 * 获取目录路径("/path/to/file.ext") === "/path/to"
 * 获取目录路径("/path/to/") === "/path"
 * 获取目录路径("/") === "/"
 * // DOS
 * 获取目录路径("c:/path/to/file.ext") === "c:/path/to"
 * 获取目录路径("c:/path/to/") === "c:/path"
 * 获取目录路径("c:/") === "c:/"
 * 获取目录路径("c:") === "c:"
 * // URL
 * 获取目录路径("http://typescriptlang.org/path/to/file.ext") === "http://typescriptlang.org/path/to"
 * 获取目录路径("http://typescriptlang.org/path/to") === "http://typescriptlang.org/path"
 * 获取目录路径("http://typescriptlang.org/") === "http://typescriptlang.org/"
 * 获取目录路径("http://typescriptlang.org") === "http://typescriptlang.org"
 * 获取目录路径("file://server/path/to/file.ext") === "file://server/path/to"
 * 获取目录路径("file://server/path/to") === "file://server/path"
 * 获取目录路径("file://server/") === "file://server/"
 * 获取目录路径("file://server") === "file://server"
 * 获取目录路径("file:///path/to/file.ext") === "file:///path/to"
 * 获取目录路径("file:///path/to") === "file:///path"
 * 获取目录路径("file:///") === "file:///"
 * 获取目录路径("file://") === "file://"
 * ```
 */
dstring 获取目录路径(dstring path) {
    path = .正规化反斜杠(path);
    // If the path provided is itself the root, then return it.
    long rootLength = .获取根长度(path);
    if (rootLength == path.length) {
        return path;
    }
    // return the leading portion of the path up to the last (non-terminal) directory separator
    // but not including any trailing directory separator.
    path = .删除尾随路径分隔符(path);
    return path[0 .. 核心.最大(rootLength, 核心.从后查找索引(path, .目录分割符))];
}
/**
 * Parse a path into an array containing a root component (at index 0) and zero or more path
 * components (at indices > 0). The result is normalized.
 * If the path is relative, the root component is `""`.
 * If the path is absolute, the root component includes the first path separator (`/`).
 *
 * ```k
 * 获取正规化路径组件组("to/dir/../file.ext", "/path/") == ["/", "path", "to", "file.ext"]
 *
 * ```
 */
dstring[] 获取正规化路径组件组(dstring 路径, dstring 当前路径 = ··null!(dstring)) {
    return .缩减路径组件(.获取路径组件组(路径, 当前路径));
}
dstring 获取正规化绝对路径(dstring 文件名, dstring 当前路径 = ··null!(dstring)) {
    return .获取路径从路径组件组(.获取正规化路径组件组(文件名, 当前路径));
}
dstring 正规化路径(dstring 路径) {
    路径 = .正规化反斜杠(路径);
    if (相对路径段正则.p·匹配(路径).empty()) {
        return 路径;
    }
    正则_!(dchar) 正则1 = ·regex(r"\/\.\/"d, "g"c);
    正则_!(dchar) 正则2 = ·regex(r"^\.\/"d, "g"c);
    dstring 简化的 = 正则2.p·替换全部(正则1.p·替换全部(路径, "/"d), ""d);
    if (简化的 != 路径) {
        路径 = 简化的;
        if (相对路径段正则.p·匹配(路径).empty()) {
            return 路径;
        }
    }
    dstring 正规路径 = .获取路径从路径组件组(.缩减路径组件(.获取路径组件组(路径)));
    if (正规路径 !is ··null!(dstring)) {
        if (.有尾随路径分割符(路径)) {
            return .确保尾随路径分隔符(正规路径);
        }
    }
    return 正规路径;
}
dstring 获取路径包括根(dstring[] 路径组件) {
    if (路径组件.length == 0) {
        return ""d;
    }
    return 核心.连接(路径组件[1 .. $], .目录分割符);
}
dstring 获取正规化绝对路径包括根(dstring 文件名, dstring 当前路径 = ··null!(dstring)) {
    return .获取路径包括根(.获取正规化路径组件组(文件名, 当前路径));
}
dstring 转为路径(dstring 文件名, dstring 基路径, dstring delegate(dstring) 获取规范文件名) {
    dstring 不正规的文件名 = .是根磁盘路径(文件名) ? .正规化路径(文件名) : .获取正规化绝对路径(文件名, 基路径);
    return 获取规范文件名(不正规的文件名);
}
路径和路径片段_ 正规化路径和片段(dstring 路径) {
    路径 = .正规化反斜杠(路径);
    dstring[] 组件 = .缩减路径组件(.获取路径组件组(路径));
    dstring 根 = 组件[0];
    组件 = 组件[1 .. $];
    if (组件.length != 0) {
        dstring 连接组件 = 根 ~ 核心.连接(组件, .目录分割符);
        return .路径和路径片段_(.有尾随路径分割符(路径) ? .确保尾随路径分隔符(连接组件) : 连接组件, 组件);
    }
    else {
        return .路径和路径片段_(根, 组件);
    }
}
/**
 * Changes the extension of a path to the provided extension if it has one of the provided extensions.
 *
 * ```k
 * 改变任意扩展("/path/to/file.ext", ".js", ".ext") == "/path/to/file.js"
 * 改变任意扩展("/path/to/file.ext", ".js", ".ts") == "/path/to/file.ext"
 * 改变任意扩展("/path/to/file.ext", ".js", [".ext", ".ts"]) == "/path/to/file.js"
 * ```
 */
dstring 改变任意扩展(dstring 路径, dstring 扩展, dstring[] 扩展组, bool 忽略大小写) {
    dstring 路径扩展 = .获取任意一个扩展名从路径(路径, 扩展组, 忽略大小写);
    return 路径扩展 ? 路径[0 .. 路径.length - 路径扩展.length] ~ (核心.开始包含(扩展, "."d) ? 扩展 : "."d ~ 扩展) : 路径;
}
//// Path Parsing
bool 是卷字符(long 字符代码) {
    return (字符代码 >= 字符_.a && 字符代码 <= 字符_.z) || (字符代码 >= 字符_.A && 字符代码 <= 字符_.Z);
}
long 获取文件URL卷分割符结尾(dstring url, long 开始位置) {
    immutable(dchar) 字符_0 = url[开始位置];
    if (字符_0 == 字符_.冒号) {
        return 开始位置 + 1;
    }
    if (字符_0 == 字符_.百分 && url[开始位置 + 1] == 字符_.数_3) {
        immutable(dchar) 字符_1 = url[开始位置 + 2];
        if (字符_1 == 字符_.a || 字符_1 == 字符_.A) {
            return 开始位置 + 3;
        }
    }
    return -1;
}
/**
 * Returns length of the root part of a path or URL (i.e. length of "/", "x:/", "//server/share/, file:///user/files").
 * If the root is part of a URL, the twos-complement of the root length is returned.
 */
long 获取编码根长度(dstring 路径) {
    if ((路径 is ··null!(dstring)) || 路径.length == 0) {
        return 0;
    }
    immutable(dchar) 字符_0 = 路径[0];
    // POSIX or UNC
    if (字符_0 == 字符_.斜杠 || 字符_0 == 字符_.反斜杠) {
        if (路径.length > 1 && 路径[1] != 字符_0) {
            return // POSIX: "/" (or non-normalized "\")
             1;
        } // POSIX: "/" (or non-normalized "\")
        long p1 = 核心.查找索引(路径, 字符_0 == 字符_.斜杠 ? .目录分割符 : .备用目录分割符, 2);
        if (p1 < 0) {
            return 路径.length;
        } // UNC: "//server" or "\\server"
        return p1 + 1; // UNC: "//server/" or "\\server\"
    }
    // DOS
    if (.是卷字符((cast(long)(字符_0))) && 路径.length > 0 && 路径[1] == 字符_.冒号) {
        immutable(dchar) 字符_1 = 路径.length > 2 ? 路径[2] : 字符_.空字符;
        if (字符_1 == 字符_.斜杠 || 字符_1 == 字符_.反斜杠) {
            return // DOS: "c:/" or "c:\"
             3;
        } // DOS: "c:/" or "c:\"
        if (路径.length == 2) {
            return // DOS: "c:" (but not "c:d")
             2;
        } // DOS: "c:" (but not "c:d")
    }
    // URL
    long 计划结束 = 核心.查找索引(路径, .Url分隔符);
    if (计划结束 != -1) {
        size_t 授权开始 = 计划结束 + Url分隔符.length;
        long 授权结束 = 核心.查找索引(路径, .目录分割符, 授权开始);
        if (授权结束 != -1) {
            // URL: "file:///", "file://server/", "file://server/path"
            // For local "file" URLs, include the leading DOS volume (if present).
            // Per https://www.ietf.org/rfc/rfc1738.txt, a host of "" or "localhost" is a
            // special case interpreted as "the machine from which the URL is being interpreted".
            dstring 计划 = 路径[0 .. 计划结束];
            dstring 授权 = 路径[授权开始 .. 授权结束];
            if (计划 == "file"d && (授权 == ""d || 授权 == "localhost"d) && .是卷字符((cast(long)(路径[授权结束 + 1])))) {
                long 卷分割结束 = .获取文件URL卷分割符结尾(路径, 授权结束 + 2);
                if (卷分割结束 != -1) {
                    if (路径[卷分割结束] == 字符_.斜杠) {
                        // URL: "file:///c:/", "file://localhost/c:/", "file:///c%3a/", "file://localhost/c%3a/"
                        return ~(卷分割结束 + 1);
                    }
                    if (卷分割结束 == 路径.length) {
                        // URL: "file:///c:", "file://localhost/c:", "file:///c$3a", "file://localhost/c%3a"
                        // but not "file:///c:d" or "file:///c%3ad"
                        return ~卷分割结束;
                    }
                }
            }
            return ~(授权结束 + 1); // URL: "file://server/", "http://server/"
        }
        return ~路径.length; // URL: "file://server", "http://server"
    }
    // 相对路径
    return 0;
}
//// Path Comparisons
比较结果_ 比较路径平台(dstring a, dstring b, 比较结果_ delegate(dstring, dstring) 比较器) {
    if (a == b) {
        return 比较结果_.等于;
    }
    if (a is ··null!(dstring)) {
        return 比较结果_.小于;
    }
    if (b is ··null!(dstring)) {
        return 比较结果_.大于;
    }
    // NOTE: Performance optimization - shortcut if the root segments differ as there would be no
    //       need to perform path reduction.
    dstring a根 = a[0 .. .获取根长度(a)];
    dstring b根 = b[0 .. .获取根长度(b)];
    比较结果_ 文本比较 = 核心.比较文本不区分大小写(a根, b根);
    if (文本比较 != 比较结果_.等于) {
        return 文本比较;
    }
    // NOTE: Performance optimization - shortcut if there are no relative path segments in
    //       the non-root portion of the path
    dstring a剩余 = a[a根.length .. $];
    dstring b剩余 = b[b根.length .. $];
    if (相对路径段正则.p·匹配(a剩余).empty() && 相对路径段正则.p·匹配(b剩余).empty()) {
        return 比较器(a剩余, b剩余);
    }
    // The path contains a relative path segment. Normalize the paths and perform a slower component
    // by component comparison.
    dstring[] a组件 = .缩减路径组件(.获取路径组件组(a));
    dstring[] b组件 = .缩减路径组件(.获取路径组件组(b));
    long 共同长度 = 核心.最小(a组件.length, b组件.length);
    {
        for (int i = 1; i < 共同长度; ++i) {
            比较结果_ 结果 = 比较器(a组件[i], b组件[i]);
            if (结果 != 比较结果_.等于) {
                return 结果;
            }
        }
    }
    return 核心.比较器比较值(a组件.length, b组件.length);
}
/**
 * Performs a case-insensitive comparison of two paths.
 */
比较结果_ 比较路径不区分大小写(dstring a, dstring b) {
    return .比较路径平台(a, b, cast(比较结果_ delegate(dstring, dstring))(·fd(&核心.比较文本不区分大小写)));
}
/**
 * Performs a case-insensitive comparison of two paths.
 */
比较结果_ 比较路径区分大小写(dstring a, dstring b) {
    return .比较路径平台(a, b, cast(比较结果_ delegate(dstring, dstring))(·fd(&核心.比较文本区分大小写)));
}
比较结果_ delegate(dstring, dstring) 获取文本比较器(bool 忽略大小写 = ··null!(bool)) {
    return 忽略大小写 ? cast(比较结果_ delegate(dstring, dstring))(·fd(&核心.比较文本不区分大小写)) : cast(比较结果_ delegate(dstring, dstring))(·fd(&核心.比较文本区分大小写));
}
比较结果_ 比较路径(dstring a, dstring b, dstring 当前目录 = ··null!(dstring), bool 忽略大小写 = ··null!(bool)) {
    a = 当前目录 ? .组合路径(当前目录, [a]) : .正规化路径(a);
    b = 当前目录 ? .组合路径(当前目录, [b]) : .正规化路径(b);
    return .比较路径平台(a, b, .获取文本比较器(忽略大小写));
}
bool 包含路径(dstring 父路径 = ··null!(dstring), dstring 子路径 = ··null!(dstring), dstring 当前目录 = ··null!(dstring), bool 忽略大小写 = ··null!(bool)) {
    if ((父路径 is ··null!(dstring)) || (子路径 is ··null!(dstring))) {
        return false;
    }
    父路径 = 当前目录 ? .组合路径(当前目录, [父路径]) : .正规化路径(父路径);
    子路径 = 当前目录 ? .组合路径(当前目录, [子路径]) : .正规化路径(子路径);
    if (父路径 == 子路径) {
        return true;
    }
    dstring[] 父路径组件 = .缩减路径组件(.获取路径组件组(父路径));
    dstring[] 子路径组件 = .缩减路径组件(.获取路径组件组(子路径));
    if (子路径组件.length < 父路径组件.length) {
        return false;
    }
    比较结果_ delegate(dstring, dstring) 组件相等比较器 = .获取文本比较器(忽略大小写);
    {
        for (int i = 0; i < 父路径组件.length; ++i) {
            比较结果_ delegate(dstring, dstring) 比较器 = i == 0 ? cast(比较结果_ delegate(dstring, dstring))(·fd(&核心.比较文本不区分大小写)) : cast(比较结果_ delegate(dstring, dstring))(组件相等比较器);
            if (比较器(父路径组件[i], 子路径组件[i]) != 比较结果_.等于) {
                return false;
            }
        }
    }
    return true;
}
/**
 * Determines whether `fileName` starts with the specified `directoryName` using the provided path canonicalization callback.
 * Comparison is case-sensitive between the canonical paths.
 *
 * @deprecated Use `containsPath` if possible.
 */
bool 开始包括目录(dstring 文件名, dstring 目录名, 核心.获取正规文件名函数_ 获取正规文件名) {
    dstring 正规文件名 = 获取正规文件名(文件名);
    dstring 正规目录名 = 获取正规文件名(目录名);
    return 核心.开始包含(正规文件名, 正规目录名 ~ "/"d) || 核心.开始包含(正规文件名, 正规目录名 ~ "\\"d);
}
dstring[] 获取路径组件组到相对路径(dstring 源路径, dstring 目标路径, 比较结果_ delegate(dstring, dstring) 文本相等比较器, 核心.获取正规文件名函数_ 获取正规文件名) {
    dstring[] 源路径组件 = .缩减路径组件(.获取路径组件组(源路径));
    dstring[] 目标路径组件 = .缩减路径组件(.获取路径组件组(目标路径));
    int 开始 = 0;
    for (开始 = 0; 开始 < 源路径组件.length && 开始 < 目标路径组件.length; ++开始) {
        dstring 源组件 = 获取正规文件名(源路径组件[开始]);
        dstring 目标组件 = 获取正规文件名(目标路径组件[开始]);
        比较结果_ delegate(dstring, dstring) 比较器 = 开始 == 0 ? cast(比较结果_ delegate(dstring, dstring))(·fd(&核心.比较文本不区分大小写)) : cast(比较结果_ delegate(dstring, dstring))(文本相等比较器);
        if (比较器(源组件, 目标组件) != 比较结果_.等于) {
            break;
        }
    }
    if (开始 == 0) {
        return 目标路径组件;
    }
    dstring[] 组件组 = 目标路径组件[开始 .. $];
    dstring[] 相对路径 = 核心.创建数组!(dstring)();
    for (; 开始 < 源路径组件.length; ++开始) {
        相对路径 = .追加!(dstring)(相对路径, ".."d);
    }
    return .追加!(dstring)(相对路径, 组件组);
}
dstring 获取相对路径从目录(dstring 源目录, dstring 目标路径, 核心.获取正规文件名函数_ 获取正规文件名, bool 忽略大小写 = false) {
    dstring[] 路径组件 = .获取路径组件组到相对路径(源目录, 目标路径, 忽略大小写 ? cast(比较结果_ delegate(dstring, dstring))(·fd(&核心.比较文本不区分大小写)) : cast(比较结果_ delegate(dstring, dstring))(·fd(&核心.比较文本区分大小写)), cast(核心.获取正规文件名函数_)(获取正规文件名));
    return .获取路径从路径组件组(路径组件);
}
/**
 * Combines and resolves paths. If a path is absolute, it replaces any previous path. Any
 * `.` and `..` path components are resolved. Trailing directory separators are preserved.
 *
 * ```k
 * resolvePath("/path", "to", "file.ext") === "path/to/file.ext"
 * resolvePath("/path", "to", "file.ext/") === "path/to/file.ext/"
 * resolvePath("/path", "dir", "..", "to", "file.ext") === "path/to/file.ext"
 * ```
 */
dstring 相对路径(dstring 路径, dstring[] 路径组件) {
    路径组件 = 路径组件.dup;
    return .正规化路径(路径.length != 0 ? .组合路径(路径, 路径组件) : .正规化反斜杠(路径));
}
/** convertToRelativePath */
dstring 转换到相对路径(dstring absoluteOrRelativePath, dstring basePath, dstring delegate(dstring) getCanonicalFileName) {
    return !.是根磁盘路径(absoluteOrRelativePath) ? absoluteOrRelativePath : .获取相对路径到目录或URL(basePath, absoluteOrRelativePath, basePath, cast(核心.获取正规文件名函数_)(getCanonicalFileName), /*绝对路径是URL*/ false);
}
// getRelativePathToDirectoryOrUrl
dstring 获取相对路径到目录或URL(dstring 目录路径或URL, dstring 相对或绝对路径, dstring 当前目录, 核心.获取正规文件名函数_ 获取正规文件名, bool 绝对路径是URL) {
    dstring[] 路径组件 = .获取路径组件组到相对路径(.相对路径(当前目录, [目录路径或URL]), .相对路径(当前目录, [相对或绝对路径]), cast(比较结果_ delegate(dstring, dstring))(·fd(&核心.比较文本区分大小写)), cast(核心.获取正规文件名函数_)(获取正规文件名));
    dstring 第一个组件 = 路径组件[0];
    if (绝对路径是URL && .是根磁盘路径(第一个组件)) {
        dstring 前缀 = 第一个组件[0] == 目录分割符[0] ? "file://"d : "file:///"d;
        路径组件[0] = 前缀 ~ 第一个组件;
    }
    return .获取路径从路径组件组(路径组件);
}
//// Path Traversal
/**
 * Calls `回调` on `目录` and every ancestor 目录 it has, returning the first defined result.
 */
T 循环每个祖先目录(T)(dstring 目录, T delegate(dstring) 回调) {
    while(true) {
        T 结果 = 回调(目录);
        if (结果 !is ··null!(T)) {
            return 结果;
        }
        dstring 父路径 = .获取目录路径(目录);
        if (父路径 == 目录) {
            return ··null!(T);
        }
        目录 = 父路径;
    }
}
unittest {
    void main() {
        dstring str = "d:/a/b/c/fi.txt"d;
        dstring 文件名 = .获取基础文件名称(str, ["txt"d, "json"d]);
        assert(文件名 == "fi"d, "'获取基础文件名称' 错误"c);
    }
    main();
}
unittest {
    void main() {
        dstring str = `abc\def\esn/qew.txt`d;
        str = .正规化反斜杠(str);
        assert(str == "abc/def/esn/qew.txt"d, "正规反斜杠错误"c);
        dstring str2 = "abc\\def\\esn/qew.txt"d;
        str2 = .正规化反斜杠(str2);
        assert(str2 == "abc/def/esn/qew.txt"d, "正规反斜杠错误"c);
        dstring[] strs = ["abc"d, "def"d, ".."d, "esn"d, "qew.txt"d];
        strs = .缩减路径组件(strs);
        assert(strs.length == 3, "'缩减路径组件' 错误"c);
        assert(strs[0] == "abc"d, "'缩减路径组件' 错误"c);
        assert(strs[1] == "esn"d, "'缩减路径组件' 错误"c);
        assert(strs[2] == "qew.txt"d, "'缩减路径组件' 错误"c);
    }
    main();
}
unittest {
    void main() {
        dstring str = "sdfasdikjzodnfo阿士大夫撒地方"d;
        long index = 核心.查找索引(str, "阿士大"d, 5);
        assert(index == 15, "查找索引错误."c);
        dstring str1 = str[index .. $];
        assert(str1 == "阿士大夫撒地方"d, "索引分割错误."c);
        long index2 = 核心.查找索引(str, "阿大士"d, 5);
        assert(index2 == -1, "查找索引错误."c);
        long index3 = 核心.查找索引(str, "sd"d);
        assert(index3 == 0, "查找索引错误."c);
        dstring str2 = str[index3 .. $];
        assert(str2 == str, "索引分割错误."c);
        long index4 = 核心.查找索引(str, "地"d);
        dstring str3 = str[0 .. index4];
        assert(str3 == "sdfasdikjzodnfo阿士大夫撒"d, "索引分割错误."c);
    }
    main();
}
unittest {
    void main() {
        dstring[] 组件 = .获取路径组件组("http://klang.org/path/to/file.ext"d);
        assert(组件.length == 4, "获取路径组件错误1"c);
        assert(组件[0] == "http://klang.org/"d, "获取路径组件错误2"c);
        assert(组件[1] == "path"d, "获取路径组件错误2"c);
        assert(组件[2] == "to"d, "获取路径组件错误2"c);
        assert(组件[3] == "file.ext"d, "获取路径组件错误2"c);
    }
    main();
}
unittest {
    void main() {
        bool b = .是URL("https://www.ietf.org/rfc/rfc1738.txt"d);
        assert(b, "是URL错误"c);
        dstring 路径 = "../源码/index.k"d;
        bool b2 = .是相对路径(路径);
        assert(b2, "是相对路径错误"c);
        bool b3 = .是绝对路径(路径);
        assert(!b3, "是绝对路径错误"c);
        dstring 路径2 = "d:/源码/index.k"d;
        bool b4 = .是绝对路径(路径2);
        assert(b4, "是绝对路径错误"c);
        bool b5 = .是相对路径(路径2);
        assert(!b5, "是相对路径错误"c);
    }
    main();
}
unittest {
    void main() {
        dstring p = "f:/a/b/c/fi.txt"d;
        dstring p1 = "f:/a/b/c\\fi.txt"d;
        比较结果_ 结果 = .比较路径(p, p1);
        assert(结果 == 比较结果_.等于, "路径比较错误"c);
    }
    main();
}
